package com.example.run_game;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.location.Location;
import android.location.LocationManager;
import android.media.MediaPlayer;
import android.util.Log;
import android.widget.Toast;

public class RunSesh {	
	private Run rungui;
	private GPSTracker gprsTracker;
	private LocationManager locationManager;
	private ScoreManager sm;
	private SoundManager sounds;
	private Player player;
	private int timeLimit;
	private AchievementManager am;
	private SubObjectiveManager sob;
	private Context cont;
	Checkpoint mostRecentHit;
	Utils utils;

	private int weatherCode;
	private int windDirection;
	private ArrayList<Checkpoint> checkpoints;
	private Checkpoint[] allCps;
	private Choice currChoice;
	private Checkpoint start;
	
	private double distance = 0;
	private long startTime = 0;
	private double avRunSpeed;
	double altGain =0;
	int calories =0;
	int distLimit;
	  
	private final double limitLeniency = 0.1;
	private boolean runBegun = false;

	RunSesh(Context context, LocationManager locIn, Run runguiIn, int maxDistIn, int maxTimeIn, SoundManager soundsIn, String[] weatherIn)
	{
		Log.d("GAME:","New Game Started");
	    sounds = soundsIn;
	    rungui = runguiIn;
	    cont = context;
	    
	    locationManager = locIn;
	    weatherCode = -1;
	    windDirection = -1;
	    if(weatherIn[0] != null && !weatherIn[0].equals(""))
	    {
	    	weatherCode = Integer.parseInt(weatherIn[0]);
	    	windDirection = Integer.parseInt(weatherIn[1]);
	    }
	    
	    DbManager db = new DbManager(context);
	    player = new Player(db);
	    am = new AchievementManager(db);
	    sm = new ScoreManager(player, rungui, sounds);
	    allCps = db.getAllCheckpoints();
	    sob = new SubObjectiveManager(db, allCps, player, rungui);
	    player.setRunGui(rungui);
	    
	    rungui.setProgressBarExp(player.getProgressBarPercentage());
	    rungui.setLevelUp(player.getLevel());

	    checkpoints = new ArrayList<Checkpoint>();	    	       	    
	    avRunSpeed = player.getAvSpeed();
	    
	    if(maxTimeIn != 0)	  
	    {
	    	timeLimit = maxTimeIn;
	    	distLimit = (int)(maxTimeIn * player.getAvSpeed());	 
	    }
	    else
	    {
	    	timeLimit = -1;
	    	distLimit = maxDistIn;
	    }
    
	    player.printDetails();
	    Log.d("GAME: ", "Limit: " + distLimit);
	    GPRSinit();	      	
	}
		
	public void checkPointEntered(Checkpoint inChk)
	{
		String[] tracks = {""};
		if(inChk.equals(start))
		{	    	 
	    	long endTime = new Date().getTime();
			player.runCompleted(distance, endTime-startTime);
			Log.d("GAME:","Starting Point");
			Log.d("GAME:", "Game Finished");
			Toast.makeText(rungui.getApplicationContext(), "You've made it back to where you started. Run stopped!",Toast.LENGTH_LONG).show();
			tracks[0] = "run_complete.mp3";
			sounds.playSound(tracks);

			player.endGame(distance, (endTime-startTime)/1000, altGain, calories, avRunSpeed, -1);
			am.saveAchievements();
			
			rungui.stopButton.performClick();
		}
		else
		{
			Log.d("GAME:", inChk.name+" reached");
			checkpoints.add(inChk);
			inChk.lastVisited = checkpoints.size();
			inChk.counter++;
			rungui.setCheckpoints(checkpoints.size()-1);
			mostRecentHit = inChk;
			
			Toast.makeText(rungui.getApplicationContext(), "Checkpoint Reached: "+ inChk.name,Toast.LENGTH_LONG).show();
			
			tracks[0] = "checkpoint_reached.mp3";
			sounds.playSound(tracks);
			
			currChoice = getNewChoice(inChk.location);
			currChoice.getAudioDescription();
			
			String[] audioString = currChoice.getAudioDescription();	
	    	String[] checkpointTracks = {"","","","","","","","",""};
	    	for(int i=0;i<audioString.length;i++)
	    	{
	    		checkpointTracks[i] = Utils.getAudio(audioString[i]);
	    		System.out.println(checkpointTracks[i]);
	    	}
	    	sounds.playSound(checkpointTracks);

		}
	}
	
	public void actOnLocationUpdate(Location previousLoc, Location loc)
	{
		if(!runBegun)
		{
	    	start = new Checkpoint(25, "The place where this run began", "Starting Point", loc);
	    	runBegun = true;	    	
	    	startTime = new Date().getTime();
	    	checkpoints.add(start);
	    	currChoice = getNewChoice(loc);
	    		  
			Toast.makeText(rungui.getApplicationContext(), "Start running!",Toast.LENGTH_LONG).show();
			String[] tracks = {"",""};
			tracks[0] = "run_started.mp3";
			sounds.playSound(tracks);
	    	mostRecentHit = start;	
	    	
	    	String[] audioString = currChoice.getAudioDescription();	
	    	String[] checkpointTracks = {"","","","","","","","",""};
	    	for(int i=0;i<audioString.length;i++)
	    	{
	    		checkpointTracks[i] = Utils.getAudio(audioString[i]);
	    		System.out.println(checkpointTracks[i]);
	    	}
	    	sounds.playSound(checkpointTracks);
	    	//String[] tracks = {"run_started.mp3","sea_walls.mp3","xp_bonus.mp3"};
	    	
	    	/*AssetFileDescriptor afd;
			try
			{
				afd = cont.getAssets().openFd("sounds/beep.mp3");
				MediaPlayer mp = new MediaPlayer();
				mp.reset();
				mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
		    	mp.prepare();
		    	int duration = mp.getDuration();
		    	System.out.println("TRACK DURATION::::::::::::::::::::::::::::::::"+duration);
		    	mp.start();
			} catch (IOException e)
			{
				e.printStackTrace();
			}*/
	    	    	
	    	//return;
		}		   
	    
		double distanceMoved = loc.distanceTo(previousLoc);	
	    
	    Checkpoint reachedCheckpoint = currChoice.updateLocModel(loc);
    	updateValues(distanceMoved, previousLoc, loc);   	   	   	
    	sm.updateScore(distanceMoved, currChoice, avRunSpeed, currChoice.getPlayerDirectionBearing(), windDirection); 
    	
    	long currentTime = new Date().getTime();
    	sob.testSubObjectives(player, loc, previousLoc, distance, (int)(currentTime - startTime), avRunSpeed, calories);
    	//TODO fix weather parameter
    	am.testAchievements(player, altGain, avRunSpeed, (currentTime - startTime), distance, allCps, weatherCode);
    	
	    if(reachedCheckpoint != null)
	    {
	    	if(!mostRecentHit.name.equals(reachedCheckpoint.name))
	    		checkPointEntered(reachedCheckpoint);
	    }
	}
	
	private void updateValues(double distanceMoved, Location previousLoc, Location loc)
	{
    	double gainInAlt = loc.getAltitude() - previousLoc.getAltitude();
    	if(gainInAlt < 0)
    		gainInAlt = 0;
    	
    	altGain += gainInAlt;    	
    	distance += distanceMoved;
    	calories += Utils.calcCalories(distanceMoved, (int)player.getWeight());
    	    	
    	long current = new Date().getTime();
    	long timePassed = current - startTime;
    	
    	if(timePassed == 0)
    		avRunSpeed = 0;   
    	else 
    		avRunSpeed = (1.0 * distance * 1000) / timePassed;	 	
    
    	    	
    	rungui.setKMDistance(distance);
    	rungui.setSpeed(avRunSpeed);
    	rungui.setCalories(calories);
    	rungui.setTargetPercent((int) (distance*100/distLimit) );
	}

	private Choice getNewChoice(Location loc)
	{
    	Log.d("GAME:", "Getting new Choice");
    	ArrayList<Checkpoint> possibles = new ArrayList<Checkpoint>();
    	ArrayList<Checkpoint> chosen = new ArrayList<Checkpoint>();
    	boolean full = false;
    	
    	//Sift those checkpoints which are in range
    	double upperbound = (distLimit-distance)*(1+limitLeniency);
    	for(int i=0; i<allCps.length; i++)    
    		if(allCps[i].isViableChoice(checkpoints.get(checkpoints.size()-1), start, upperbound))   	
    		{
    			if (mostRecentHit == null)
    				possibles.add(allCps[i]);
    			else if (!allCps[i].name.equals(mostRecentHit.name))
    				possibles.add(allCps[i]);    
    		}
    	
    	//Log.d("GAME: ", "PossSize "+possibles.size());    	
    	//Sort those checkpoints by distance and visitations
     	Collections.sort(possibles, new CheckpointComparator(checkpoints.size(), checkpoints.get(checkpoints.size()-1 )));
     	
     	//For all other checkpoints, add only if they are more than 40 degrees away from other chosen checkpoints
    	for(int i=0; i<possibles.size(); i++)
		{    		
    		//Log.d("GAME:", "Looking at "+possibles.get(i).name);
    		if(spreadEnough(loc, possibles.get(i), chosen))    		
    			chosen.add(possibles.get(i));    		
    		if(chosen.size() == 3)
    		{
    			full = true;
    			break;
    		}
		}      		
    	if(full)
    	{
    		Checkpoint[] chosenArray = new Checkpoint[3];
    		chosen.toArray(chosenArray);
    		return new Choice(chosenArray, loc, locationManager, (distance/(distLimit)*100)); 
    	}
    	else if(possibles.size() >= 3)
    	{
    		//If checkpoints are not available that are neatly spread, then just pick the closest ones 
    		return new Choice(new Checkpoint[] {possibles.get(0), possibles.get(1), possibles.get(2)}, loc, locationManager,(distance/(distLimit)*100));  
    	}
    	else
    		return new Choice(new Checkpoint[] {start}, loc, locationManager,(distance/(distLimit)*100));     	    	  
	}

    private boolean spreadEnough(Location loc, Checkpoint possible, ArrayList<Checkpoint> alreadyChosen) 
    {
    	boolean spread = true;
		for(int i=0; i<alreadyChosen.size(); i++)
		{    			
			double bearingToCand = loc.bearingTo(possible.location);
			double bearingToChosen = loc.bearingTo(alreadyChosen.get(i).location);
			double diff = Math.abs(bearingToCand - bearingToChosen);
			//Log.d("GAME:", "Diff: "+diff+" from "+alreadyChosen.get(i).name);
			double distanceAway = (int) loc.distanceTo(possible.location);
			double angle;
			if(distanceAway < 500)
				angle = 40;
			else 
				angle =  1.0/distanceAway * 20000.0;
			//Log.d("GAME:", "Angle needed: " + angle);
			if(diff < angle || diff > (360-angle))			
				spread = false;					
		}
		//if(spread)
			//Log.d("GAME:", possible.name + " added");
		return spread;
    }
    
    public ScoreManager getScoreManager()
    {
    	return sm;
    }

    public GPSTracker getGPSTracker()
    {
      return gprsTracker;
    }
	
	private void GPRSinit()
    {
        final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
		if (!gpsEnabled)
			rungui.warnUserGPSDisabled();
		gprsTracker = new GPSTracker(locationManager, rungui.getGPSHandler());		       
    }

	public Player getPlayer()
	{
		return player;
	}

	public String getTarget()
	{
		DecimalFormat df = new DecimalFormat("#.#");
		String result = timeLimit==-1?(df.format(distLimit*1.0/1000)+"km"):(timeLimit/60+" minutes");
		return result;
	}
	
	public Choice getCurrChoice()
	{
		return currChoice;
	}

	public ArrayList<Checkpoint> getCheckpoints()
	{
		return checkpoints;
	}
	
	public int getCalories()
	{
		return calories;
	}
	
	public SubObjectiveManager getSob()
	{
		return sob;
	}
	
	public ScoreManager getSM()
	{
		return sm;
	}
}
